﻿using System;
using System.Linq;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.IO;
using System.Diagnostics;

[LoadInSeparateAppDomainAttribute]
public class AppDomainWrapperTask : AppDomainIsolatedTask
{
	public AppDomainWrapperTask()
	{
		ArrayNames = ParamNames = new ITaskItem[0];
	}

	[Required]
	public string Task { get; set; }

	public ITaskItem[] ParamNames { get; set; }
	public ITaskItem[] ArrayNames { get; set; }

	public string Bin { get; set; }

	public string Param1 { get; set; }
	public string Param2 { get; set; }
	public string Param3 { get; set; }
	public string Param4 { get; set; }
	public string Param5 { get; set; }
	public string Param6 { get; set; }
	public string Param7 { get; set; }
	public string Param8 { get; set; }
	public string Param9 { get; set; }

	public ITaskItem[] Array1 { get; set; }
	public ITaskItem[] Array2 { get; set; }
	public ITaskItem[] Array3 { get; set; }
	public ITaskItem[] Array4 { get; set; }
	public ITaskItem[] Array5 { get; set; }
	public ITaskItem[] Array6 { get; set; }
	public ITaskItem[] Array7 { get; set; }
	public ITaskItem[] Array8 { get; set; }
	public ITaskItem[] Array9 { get; set; }

	[Output]
	public ITaskItem[] Include { get; set; }

	[Output]
	public ITaskItem[] Reference { get; set; }

	public override bool Execute()
	{
		Log.LogMessage(MessageImportance.High, "* TaskWrapper: Execute. Params = " + ParamNames.Length);

		Type taskType;
		try
		{
			AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
			taskType = Type.GetType(Task);
		}
		finally
		{
			AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
		}

		if(taskType==null)
		{
			throw new Exception("Type '" + Task + "' does not found");
		}

		var task = (AppDomainIsolatedTaskWraper)Activator.CreateInstance(taskType);

		task.LogMessage = msg => Log.LogMessage(MessageImportance.High, "* Task " + msg);

		for(var i = 0; i < ParamNames.Length; i++)
		{
			var paramName = ParamNames[i];
			// Log.LogMessage(MessageImportance.High, "# Param " + paramName.ItemSpec);

			var value = GetType().GetProperty("Param" + (i + 1)).GetValue(this, null);
			var pi = taskType.GetProperty(paramName.ItemSpec);
			pi.SetValue(task, value, null);
		}

		for(var i = 0; i < ArrayNames.Length; i++)
		{
			var arrayName = ArrayNames[i];
			// Log.LogMessage(MessageImportance.High, "# Array " + arrayName.ItemSpec);

			var value = GetType().GetProperty("Array" + (i + 1)).GetValue(this, null);
			taskType.GetProperty(arrayName.ItemSpec).SetValue(task, value, null);
		}

		var result = task.Execute();

		Include = task.Include.ToArray();
		Reference = task.Reference.ToArray();
		foreach(var file in Include)
		{
			Log.LogMessage(MessageImportance.High, "* TaskWrapper: Include: " + file);
		}

		return result;
	}

	Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
	{
		var i = Task.IndexOf(',');
		if(i==-1)
		{
			throw new Exception("Specify task name with assembly, like 'MyTask, MyAssembly.dll'");
		}
		var file = Path.Combine(Bin, Task.Substring(i).Trim().Trim(',').Trim());
		var asm = Assembly.LoadFrom(file);
		Debug.Assert(asm != null);
		return asm;
	}
}

